﻿using DataAccessLayer;
using Microsoft.ApplicationServer.Caching;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Web;
using System.Xml;
using System.Xml.Linq;

namespace WCFServiceWebRole1
{

    public class APIKeyAuthorization : ServiceAuthorizationManager
    {
        public const string APIKEY = "APIKey";
        public const string APIKEYLIST = "APIKeyList";
        public bool VERIFICATION = true;
        DataCache myCache;
        public static DeviceEntity deviceEntity = new DeviceEntity();

        //public DataCache getCacheObject()
        //{

        //    // Caching Code
        //    // Create a DataCacheFactoryConfiguration object
        //    DataCacheFactoryConfiguration config = new DataCacheFactoryConfiguration();

        //    // Enable the AutoDiscorveryProperty (and any other required configuration settings):
        //    config.AutoDiscoverProperty = new DataCacheAutoDiscoverProperty(true, "WCFServiceWebRole1");

        //    // Create a DataCacheFactory object with the configuration settings:
        //    DataCacheFactory factory = new DataCacheFactory(config);

        //    // Use the factory to create a DataCache client for the "default" cache:
        //    myCache = factory.GetCache("default");

        //    return myCache;

        //}

        public List<Guid> APIKeys
        {
            get
            {
                // Get Recent APIKeys from the cache
                myCache = WebRole.getCacheObject();
                var keys = myCache.Get(APIKEYLIST) as List<Guid>;
                return keys;
            }
        }

        private void AddAPIKeyToCache(Guid newkey)
        {
            myCache = WebRole.getCacheObject();
            myCache.Put(APIKEYLIST, newkey.ToString());       
        }

        protected override bool CheckAccessCore(OperationContext operationContext)
        {  
            return IsValidAPIKey(operationContext);
        }

        public bool IsValidAPIKey(OperationContext operationContext)
        {
            Guid apiKey;

            // if verification is disabled, return true
            if (!VERIFICATION)
                return true;

            //Get the key from the Authorization header of current request
            string key = GetAPIKey(operationContext);

            // Convert the string into a Guid and validate it
            if (Guid.TryParse(key, out apiKey))
            {

                //If APIKeylist is not found in the Cache, create a new one and store it in the cache
                if (APIKeys == null)
                {
                    if (deviceEntity.checkApikeyExist_DAL(key))
                    {

                        //List<Guid> keyList = new List<Guid>();
                        //myCache.Add(APIKEYLIST, keyList);

                        AddAPIKeyToCache(apiKey);
                        return true;
                    }
                }
             
                //Check if the key exists in the cache             
                if (APIKeys.Contains(apiKey) && APIKeys != null)
                {
                    return true;
                }
                //If not found in the cache, and if exists in the repository then add it in the cache
                else if(deviceEntity.checkApikeyExist_DAL(key))
                {
                    AddAPIKeyToCache(apiKey);
                    return true;
                }
                else
                {
                    CreateErrorReply(operationContext, key);
                    return false;
                }
            }
            //The provided APIKey is not proper Guid format
            else
            {
                // Send back an HTML reply
                CreateInvalidGuidErrorReply(operationContext, key);
                return false;
            }
        }

        public string GetAPIKey(OperationContext operationContext)
        {
            // Get the request message
            OperationContext currentContext = OperationContext.Current;

            // Get the HTTP Request
            HttpRequestMessageProperty reqMsg = currentContext.IncomingMessageProperties["httpRequest"] as HttpRequestMessageProperty;

            // Get the authorization header
            string authToken = reqMsg.Headers["Authorization"];


            //Getting the APIKey from the Authorization header
            string apikey=null;
            string[] data = authToken.Split(' ');
            if (data.Length == 2 && data.ElementAt<string>(0) == "APIKey")
            {
                apikey = data.ElementAt<string>(1);
            }
           
            // Return the API key (if present, null if not)
            return apikey;
        }

        private static void CreateErrorReply(OperationContext operationContext, string key)
        {
            // The error message is padded so that IE shows the response by default
            using (var sr = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + APIErrorHTML))
            {
                XElement response = XElement.Load(sr);
                using (Message reply = Message.CreateMessage(MessageVersion.None, null, response))
                {
                    HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.Unauthorized, StatusDescription = String.Format("'{0}' is an invalid API key", key) };
                    responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";
                    reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
                    operationContext.RequestContext.Reply(reply);
                    // set the request context to null to terminate processing of this request
                    operationContext.RequestContext = null;
                }
            }
        }

        private static void CreateInvalidGuidErrorReply(OperationContext operationContext, string key)
        {
            // The error message is padded so that IE shows the response by default
            using (var sr = new StringReader("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + APIInvalidHTML))
            {
                XElement response = XElement.Load(sr);
                using (Message reply = Message.CreateMessage(MessageVersion.None, null, response))
                {
                    HttpResponseMessageProperty responseProp = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.Unauthorized, StatusDescription = String.Format("'{0}' is an invalid API key", key) };
                    responseProp.Headers[HttpResponseHeader.ContentType] = "text/html";
                    reply.Properties[HttpResponseMessageProperty.Name] = responseProp;
                    operationContext.RequestContext.Reply(reply);
                    // set the request context to null to terminate processing of this request
                    operationContext.RequestContext = null;
                }
            }
        }

        public const string APIErrorHTML = @"
<html>
<head>
    <title>Request Error - No API Key</title>
    <style type=""text/css"">
        body
        {
            font-family: Verdana;
            font-size: x-large;
        }
    </style>
</head>
<body>
    <h1>
        Request Error
    </h1>
    <p>
        A valid API key needs to be included using the apikey query string parameter
    </p>
</body>
</html>
";
      

        public const string APIInvalidHTML = @"
<html>
<head>
    <title>Request Error - No API Key</title>
    <style type=""text/css"">
        body
        {
            font-family: Verdana;
            font-size: x-large;
        }
    </style>
</head>
<body>
    <h1>
        Request Error
    </h1>
    <p>
        A valid API key needs to be included using the apikey query string parameter
    </p>
</body>
</html>
";
    }
}